package cn.example.game.db.dao;
import org.artifact.core.db.JedisDao;
import org.artifact.core.lang.StrPlus;
import redis.clients.jedis.Pipeline;
import org.artifact.core.context.bytebuf.IByteBuff;
import org.artifact.core.context.bytebuf.IByteBuffFactory;
import redis.clients.jedis.Jedis;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.Set;
import org.artifact.core.lang.IServer;
import java.util.function.BiFunction;
import org.artifact.core.db.BaseBean;
import cn.hutool.core.map.MapUtil;
import java.util.HashMap;
import org.artifact.core.db.BaseDao;
import cn.example.game.db.bean.Player;
import static cn.hutool.core.util.ObjectUtil.equal;

public abstract class PlayerDAO extends JedisDao<Player>{
	@Override
	protected String getDBName() {
		return "example_game";
	}
	
	@Override
	protected String getTableName() {
		return "t_player";
	}
	
	@Override
	protected Class<Player> getEntityClass(){
		return Player.class;
	}
	
	@Override
	protected Player createEntity() {
		return new Player() {};
	}

	@Override
	protected String getPKFieldName() {
		return "id";
	}
	
	// 根据主键导出
	public Map<String,Object> exportByPrimaryKey(long id,BiFunction<BaseDao,BaseBean,Object> function){
		return export(findByPrimaryKey(id),function);
	}
	
	public Map<String,Object> exportByUid(long uid,BiFunction<BaseDao,BaseBean,Object> function){
		return export(findByUid(uid),function);
	}
	public Map<String,Object> exportBySid(int sid,BiFunction<BaseDao,BaseBean,Object> function){
		return export(findBySid(sid),function);
	}
	
	public Map<String,Object> export(Player player,BiFunction<BaseDao,BaseBean,Object> function){
		Map<String,Object> data = new HashMap<>();
		data.put("self", function.apply(this,player));
		Map<String,Object> nodes = new HashMap<>();
		data.put("nodes", nodes);
		/**
		 * 此处可优化为并发加载，可选方式有executor.invokeAll、forkjoin都可以。暂时不做优化处理
		 */
		nodes.put("task.pid",getTaskDAO().exportByPid((long) player.pk(),function));
		nodes.put("friend.pid",getFriendDAO().exportByPid((long) player.pk(),function));
		nodes.put("playerMail.pid",getPlayerMailDAO().exportByPid((long) player.pk(),function));
		nodes.put("hero.pid",getHeroDAO().exportByPid((long) player.pk(),function));
		nodes.put("prop.pid",getPropDAO().exportByPid((long) player.pk(),function));
		return data;
	}
	
	public List<Map<String,Object>> export(List<Player> players,BiFunction<BaseDao,BaseBean,Object> function){
		List<Map<String,Object>> data = new ArrayList<>();
		for(Player player : players){
			data.add(export(player,function));
		}
		return data;
	}


	public Player _import(Map<String,Object> data,BiFunction<BaseDao,Map,BaseBean> function){
	    Player player = createEntity();
	    player = (Player)function.apply(this,MapUtil.get(data,"self",Map.class));

        Map<String,Object> nodes = MapUtil.get(data,"nodes",Map.class);
        /**
         * 此处可优化为并发加载，可选方式有executor.invokeAll、forkjoin都可以。暂时不做优化处理
         */
        getTaskDAO()._import(MapUtil.get(nodes,"task.pid",List.class),function);
        getFriendDAO()._import(MapUtil.get(nodes,"friend.pid",List.class),function);
        getPlayerMailDAO()._import(MapUtil.get(nodes,"playerMail.pid",List.class),function);
        getHeroDAO()._import(MapUtil.get(nodes,"hero.pid",List.class),function);
        getPropDAO()._import(MapUtil.get(nodes,"prop.pid",List.class),function);
        return player;
    }

    public List<Player> _import(List<Map<String,Object>> data,BiFunction<BaseDao,Map,BaseBean> function){
        List<Player> list = new ArrayList<>();
        for(Map<String,Object> map : data){
            list.add(_import(map,function));
        }
        return list;
    }
	
	protected abstract TaskDAO getTaskDAO();
	protected abstract FriendDAO getFriendDAO();
	protected abstract PlayerMailDAO getPlayerMailDAO();
	protected abstract HeroDAO getHeroDAO();
	protected abstract PropDAO getPropDAO();
	

	protected String[] getIndexKey(Player t){
		String[] indexKeys = new String[2];
		indexKeys[0] = StrPlus.b("uid:", t.getUid()).e();
		indexKeys[1] = StrPlus.b("sid:", t.getSid()).e();
		return indexKeys;
	}
	
	protected void addIndex(Pipeline p,Player t){
		String[] keys = indexKeys.get(t.pk());
		if(keys == null){
			keys= getIndexKey(t);
		}
		p.hset(toBytes(getTableName()+":unique"), toBytes(keys[0]),toBytes(t.pk()));
		p.hset(toBytes(getTableName()+":unique"), toBytes(keys[1]),toBytes(t.pk()));
	}
	
	protected void removeIndex(Pipeline p,Player t){
		String[] keys = indexKeys.get(t.pk());
		if(keys == null){
			keys= getIndexKey(t);
		}
		p.hdel(toBytes(getTableName()+":unique"),toBytes(keys[0]));
		p.hdel(toBytes(getTableName()+":unique"),toBytes(keys[1]));
	}
	

	public Player findByUid(long uid){
		return resultFirst(()->{
			Jedis jedis = getJedis();
			IByteBuffFactory factory = IServer.me().getContext().getByteBuffFactory();
			IByteBuff byteBuff = null;
			try{
				byte[] primaryKey = jedis.hget(toBytes(getTableName()+":unique"),toBytes(StrPlus.b("uid:", uid).e()));
				if(primaryKey != null){
					byte[] data = jedis.hget(toBytes(getTableName()), primaryKey);
					if(data!=null){
						byteBuff = factory.wrap(data);
						Map<Integer, Object> map = byteBuff.readMap();
						if (map != null) {
							return createEntity().read(map);
						}				
					}
				}
			}finally {
				jedis.close();
			}
			return null;
		}, t -> {
			return equal(t.getUid(), uid);
		}) ;
	}
	
	public Player findBySid(int sid){
		return resultFirst(()->{
			Jedis jedis = getJedis();
			IByteBuffFactory factory = IServer.me().getContext().getByteBuffFactory();
			IByteBuff byteBuff = null;
			try{
				byte[] primaryKey = jedis.hget(toBytes(getTableName()+":unique"),toBytes(StrPlus.b("sid:", sid).e()));
				if(primaryKey != null){
					byte[] data = jedis.hget(toBytes(getTableName()), primaryKey);
					if(data!=null){
						byteBuff = factory.wrap(data);
						Map<Integer, Object> map = byteBuff.readMap();
						if (map != null) {
							return createEntity().read(map);
						}				
					}
				}
			}finally {
				jedis.close();
			}
			return null;
		}, t -> {
			return equal(t.getSid(), sid);
		}) ;
	}
	
}